package com.ceair.authorization.handler;

import com.ceair.config.MyUrlConfig;
import com.ceair.entity.model.Result;
import com.ceair.util.JsonUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UriUtils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import static org.springframework.security.oauth2.core.OAuth2ErrorCodes.INVALID_REQUEST;

/**
 * @author wangbaohai
 * @ClassName ConsentAuthorizationResponseHandler
 * @description: 授权确认前后端分离适配响应处理
 * @date 2024年11月20日
 * @version: 1.0.0
 */
public class ConsentAuthorizationResponseHandler implements AuthenticationSuccessHandler {

    private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    /**
     * 当用户成功认证后调用此方法处理成功认证
     *
     * @param request        HttpServletRequest对象，包含请求信息
     * @param response       HttpServletResponse对象，用于向客户端发送响应
     * @param authentication 认证对象，包含用户认证信息
     * @throws IOException      如果在处理过程中发生I/O错误
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException {
        // 获取将要重定向的回调地址
        String redirectUri = this.getAuthorizationResponseUri(authentication);
        // 检查请求方法是否为POST，并且CONSENT_PAGE_URI是一个绝对URL
        if (request.getMethod().equals(HttpMethod.POST.name()) && UrlUtils.isAbsoluteUrl(MyUrlConfig.CONSENT_PAGE_URI)) {
            // 如果是post请求并且CONSENT_PAGE_URI是完整的地址，则响应json
            Result<String> success = Result.success(redirectUri);
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.getWriter().write(JsonUtils.objectCovertToJson(success));
            response.getWriter().flush();
            return;
        }
        // 否则重定向至回调地址
        this.redirectStrategy.sendRedirect(request, response, redirectUri);
    }

    /**
     * 根据认证信息生成授权响应URI。
     * <p>
     * 该方法用于构建OAuth2授权码流程的授权响应URI。它首先确保重定向URI和授权码不为空，
     * 然后构建包含授权码和状态参数（如果提供了状态）的URI。
     *
     * @param authentication 包含用户认证数据的认证信息。
     * @return 授权响应URI字符串。
     * @throws OAuth2AuthorizationCodeRequestAuthenticationException 如果重定向URI或授权码为空，则抛出此异常。
     */
    private String getAuthorizationResponseUri(Authentication authentication) {

        // 将认证对象转换为特定类型的OAuth2授权码请求认证对象。
        OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
                (OAuth2AuthorizationCodeRequestAuthenticationToken) authentication;

        // 检查重定向URI是否为空，如果为空则抛出异常。
        if (ObjectUtils.isEmpty(authorizationCodeRequestAuthentication.getRedirectUri())) {
            String authorizeUriError = "重定向URI不能为空";
            throw new OAuth2AuthorizationCodeRequestAuthenticationException(new OAuth2Error(INVALID_REQUEST,
                    authorizeUriError, (null)), authorizationCodeRequestAuthentication);
        }

        // 检查授权码是否为空，如果为空则抛出异常。
        if (authorizationCodeRequestAuthentication.getAuthorizationCode() == null) {
            String authorizeError = "授权码不能为空";
            throw new OAuth2AuthorizationCodeRequestAuthenticationException(new OAuth2Error(INVALID_REQUEST,
                    authorizeError, (null)), authorizationCodeRequestAuthentication);
        }

        // 使用重定向URI开始构建URI，并添加授权码作为查询参数。
        UriComponentsBuilder uriBuilder = UriComponentsBuilder
                .fromUriString(authorizationCodeRequestAuthentication.getRedirectUri())
                .queryParam(OAuth2ParameterNames.CODE,
                        authorizationCodeRequestAuthentication.getAuthorizationCode().getTokenValue());

        // 如果提供了状态参数，则将其作为查询参数添加，并进行URL编码。
        if (StringUtils.hasText(authorizationCodeRequestAuthentication.getState())) {
            uriBuilder.queryParam(
                    OAuth2ParameterNames.STATE,
                    UriUtils.encode(authorizationCodeRequestAuthentication.getState(), StandardCharsets.UTF_8));
        }

        // 完成URI构建，显式编码组件，并返回结果字符串。
        // build(true) -> 组件显式编码
        return uriBuilder.build(true).toUriString();
    }

}
